#include <linux/module.h>  
#include <linux/kernel.h>  
#include <linux/init.h>  
#include <linux/sched.h>
#include <linux/init.h>  
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define CRU_REG_PAD_SEL		(0x28100C00)
#define GPIO_BASE			(0x28006000)
#define GPIO_PORTA			(0x0)
#define GPIO_PORTB			(0xC)
#define GPIO_PORTC			(0x18)
#define GPIO_PORTD			(0x24)
#define GPIO_DATA_REG		(0x0)
#define GPIO_DATA_DIRECTION_REG		(0x4)
#define GPIO_DATA_SOURCE_REG		(0x8)
#define GPIO_EXT_PORT_A    	(0x50)
#define GPIO_GPI		(0x0F)
#define GPIO_GPO		(0xF0)

//# GPIOA4  CS
//# GPIOA5	SCK
//# GPIOA6  -> MOSI 输出
//# GPIOA7	<- MISO 输入

#define CS_0        gpio_write(0,0)     //拉低 SPI_CS
#define CS_1        gpio_write(1,0)     //拉高 SPI_CS
#define SCLK_0      gpio_write(0,1)     //拉低 SPI_SCK 
#define SCLK_1      gpio_write(1,1)     //拉高 SPI_SCK 
#define SO_0        gpio_write(0,2)     //拉低 SPI_MOSI 
#define SO_1        gpio_write(1,2)     //拉高 SPI_MOSI 
#define in_SI       (gpio_bool_read(0x80))
#define Delay_Us(N)  udelay(N)

#define UINT32	unsigned int
#define UINTN	UINT32
#define uint8 unsigned char
#define uint32 unsigned int

//#define MOD_DEBUG 
void __iomem *gpiobase;
void __iomem *rcu_sel;

static unsigned char gpio_bool_read(int mask){
  uint32_t value = 0;
	/* read GPI:<GPIOA bit0..3> */
	value = __raw_readl(gpiobase + GPIO_EXT_PORT_A);

	if(value & mask){
		return (unsigned char)1;
	}else{
		return (unsigned char)0;
	}
}

void gpio_write(int wreg, int bits){

  uint32_t value = 0;
	value = __raw_readl(gpiobase + GPIO_PORTA + GPIO_DATA_REG);
	if(wreg){
      value |= (1<<(4+bits));
  }else{
      value &= ~(1<<(4+bits));
  }
	__raw_writel(value, gpiobase + GPIO_PORTA + GPIO_DATA_REG);
}

/***************************************************************************************
读取一个字节   
*****************************************************************************************/
uint32 JustRead_bytes(void)
{
    uint8 i;
    uint32 read_data;
    
    CS_0;
    Delay_Us(5);
    for (i = 0; i < 16; i++)
    {
        SCLK_1;
		Delay_Us(4);
        read_data <<= 1;
		if (in_SI)
            read_data++;
		SCLK_0;
		Delay_Us(4);    
    }
    SCLK_0;
    CS_1;
    return (read_data);
}


/***************************************************************************************
读取一个字节   
*****************************************************************************************/
uint32 Read_status(uint8 command, uint8 command_data)
{
    uint8 i;
    uint32 write_data = 0;
    uint32 read_data = 0;
    
    write_data |= command;
    write_data = write_data << 8;
    write_data |= command_data;

	CS_0;
	Delay_Us(5);
    for (i = 0; i < 16; i++)
    {
        if (0x8000 & write_data)
            SO_1;
        else
            SO_0;
        SCLK_1;
        Delay_Us(4);
        read_data <<= 1;
        if (in_SI)
            read_data++;
		SCLK_0;
        Delay_Us(4);

        write_data <<= 1;
    }

	SCLK_0;
    CS_1;

/*
	Delay_Us(20);

	CS_0;
	Delay_Us(5);
    for (i = 0; i < 16; i++)
    {
		SCLK_1;
        Delay_Us(4);
		read_data <<= 1;
        if (in_SI)
            read_data++;
        SCLK_0;
        Delay_Us(4);
    }

    SCLK_0;
    CS_1;
*/

    return (read_data);
}

/************************
 * file_operations->read
 ************************/
static ssize_t spi_proc_read(struct file *file, char __user *buffer, size_t count, loff_t *f_pos)
{
	unsigned long offset =  *f_pos;
	uint8 *tmp;
  	int i;
	uint32 value;
	
	if(offset >= 1){
		return 0;
	}
	tmp = kzalloc(count+1, GFP_KERNEL);
	if (!tmp)
		return -ENOMEM;
	if(count >= 2)
	{
		value = JustRead_bytes();
		tmp[0]  = value >> 8;
		tmp[1] 	= value & 0xff;
		
		if (copy_to_user(buffer, tmp, count)) {
			kfree(tmp);
			return -EFAULT;
		}

		kfree(tmp);
		return 1;
	}
	kfree(tmp);
	return 0;
}
 
/************************
 * file_operations->write
 ************************/
static ssize_t spi_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *f_pos)
{
	char *tmp = kzalloc((count+1), GFP_KERNEL);
	unsigned int gpiovalue, gpiovalue1, gpiovalue2, gpiovalue3;
  int readvalue;
	if (!tmp)
		return -ENOMEM;
	if(count >= 2)
	{
		if (copy_from_user(tmp, buffer, count)) {
			kfree(tmp);
			return -EFAULT;
		}
		gpiovalue = tmp[0];
		if(gpiovalue>=0 && gpiovalue<=0xF)
			gpiovalue = gpiovalue;
		else if(gpiovalue>='0' && gpiovalue<='9')
			gpiovalue = gpiovalue - '0';
		else if(gpiovalue>='a' && gpiovalue<='f')
			gpiovalue = gpiovalue - 'a' + 10;
		else if(gpiovalue>='A' && gpiovalue<='F')
			gpiovalue = gpiovalue - 'A' + 10;

		gpiovalue1 = tmp[1];
		if(gpiovalue1>=0 && gpiovalue1<=0xF)
			gpiovalue1 = gpiovalue1;
		else if(gpiovalue1>='0' && gpiovalue1<='9')
			gpiovalue1 = gpiovalue1 - '0';
		else if(gpiovalue1>='a' && gpiovalue1<='f')
			gpiovalue1 = gpiovalue1 - 'a' + 10;
		else if(gpiovalue1>='A' && gpiovalue1<='F')
			gpiovalue1 = gpiovalue1 - 'A' + 10;
		
		gpiovalue2 = tmp[2];
		if(gpiovalue2>=0 && gpiovalue2<=0xF)
			gpiovalue2 = gpiovalue2;
		else if(gpiovalue2>='0' && gpiovalue2<='9')
			gpiovalue2 = gpiovalue2 - '0';
		else if(gpiovalue2>='a' && gpiovalue2<='f')
			gpiovalue2 = gpiovalue2 - 'a' + 10;
		else if(gpiovalue2>='A' && gpiovalue2<='F')
			gpiovalue2 = gpiovalue2 - 'A' + 10;
		
		gpiovalue3 = tmp[3];
		if(gpiovalue3>=0 && gpiovalue3<=0xF)
			gpiovalue3 = gpiovalue3;
		else if(gpiovalue3>='0' && gpiovalue3<='9')
			gpiovalue3 = gpiovalue3 - '0';
		else if(gpiovalue3>='a' && gpiovalue3<='f')
			gpiovalue3 = gpiovalue3 - 'a' + 10;
		else if(gpiovalue3>='A' && gpiovalue3<='F')
			gpiovalue3 = gpiovalue3 - 'A' + 10;
		
	printk("v1:%x,v2:%x,v3:%x,v4:%x\n", gpiovalue,gpiovalue1,gpiovalue2,gpiovalue3);
	gpiovalue  = (gpiovalue<<4) | gpiovalue1;
	gpiovalue1 = (gpiovalue2<<4) | gpiovalue3;
    printk("write commakd_%x data_%x to spi\n", gpiovalue, gpiovalue1);
    readvalue = Read_status(gpiovalue, gpiovalue1);
    
    printk("read value_first_16: %x\n", (readvalue & 0xffff0000) >> 16);
    printk("read value_secon_16: %x\n", readvalue & 0xffff);

		kfree(tmp);
		return count;
	}
	kfree(tmp);
	return 0;
}

static struct file_operations gpio_spi_fops = {
	.read	= spi_proc_read,
	.write 	= spi_proc_write,
};

static int __init init(void)                                                                                              
{        
	uint32_t value = 0;
  struct proc_dir_entry* file;
	
  file = proc_create("hwDaemon_config", 0666, NULL, &gpio_spi_fops);
	if (!file){
    printk("proc spi fops create err!\n");  
    return -ENOMEM;
  }

	rcu_sel = NULL;                                                  	
	rcu_sel = ioremap(CRU_REG_PAD_SEL, 0x4);                                                 
	printk(KERN_INFO "RCU_SEL mapped to %lx\n", (unsigned long)rcu_sel);
	/*select GPIOA[0..7] cru_sel_rst_state -> gpio func*/
	value = __raw_readl(rcu_sel);
	__raw_writel(value & (~(0x1<<4)), rcu_sel);
	
	gpiobase = NULL;                                                  	
	gpiobase = ioremap(GPIO_BASE, 0x1000);                                                 
	printk(KERN_INFO "Gpio mapped to %lx\n", (unsigned long)gpiobase);
	/* Configure GPO:<GPIOA bit4..6> as output, GPIOA7为输入 */
	value = __raw_readl(gpiobase + GPIO_PORTA + GPIO_DATA_DIRECTION_REG);
	value &= 0x0F;
	__raw_writel(value | 0x70, gpiobase + GPIO_PORTA + GPIO_DATA_DIRECTION_REG);	
	udelay(10000);
	/* Configure GPI:<GPIOA bit0..3> as input */
	value = __raw_readl(gpiobase + GPIO_PORTA + GPIO_DATA_DIRECTION_REG);
	__raw_writel(value & ~GPIO_GPI, gpiobase + GPIO_PORTA + GPIO_DATA_DIRECTION_REG);
	
	CS_1;
	SCLK_0;

	return 0;                                                                                              
}                                                                                                       
                                                                                                        
static void __exit fini(void)                                                                                              
{             
  remove_proc_entry("hwDaemon_config", NULL);          
	iounmap(rcu_sel);
	iounmap(gpiobase);            
	printk(KERN_INFO "Gpio unmapped\n");                                                          
}                                                                                                       
                                                                                                        
module_init(init);                                                                                      
module_exit(fini); 
MODULE_AUTHOR("HW");        
MODULE_LICENSE("GPL");
